from network import QKDNetwork
import numpy as np

# 计算Jain's系数
def calculate_jain_fairness(users):
    n = len(users)
    sum_x = sum(users)
    sum_x_squared = sum(map(lambda x: x**2, users))
    jain_index = (sum_x**2) / (n * sum_x_squared)
    return jain_index

# 在线Greedy计算
def onlineGreedy(n: QKDNetwork, traffic_matrix):
    # 依次断开每条边，测各种性能指标
    # 直接最短路径，输入网络和业务量矩阵，输出alpha的值（最大能满足的业务量系数）
    nodeNum = len(n.nodeList)
    # print(n.getLinkCapacity(1,2))
    alphas = []
    resourceUtilizations = []
    for break_start_node in range(nodeNum):
        for break_end_node in n.getNode(break_start_node + 1).adjacentNodes:
            flowMatrix = [[0] * nodeNum for _ in range(nodeNum)]
            alpha = 0
            resourceUtilization = 0  # 资源利用率
            hasDetourPath = True
            if(break_start_node + 1 == break_end_node):
                # 如果破碎的起点等于终点，则跳过
                continue
            # 破碎的起点ID：break_start_node + 1
            # 破碎的终点ID：break_end_node
            for i in range(nodeNum):
                for j in range(nodeNum):
                    if(i == j):
                        continue
                    tmpShortestPath = n.getGreedyPathWithFlowMatrix(i + 1, j + 1, flowMatrix) # 获取最短路径
                    tmpShortestPath.insert(0, i + 1)

                    for spIndex in range(len(tmpShortestPath) - 1):
                        if(tmpShortestPath[spIndex] == break_start_node + 1 and tmpShortestPath[spIndex + 1] == break_end_node):
                            # 如果路断了，那么从断开处再计算一个最短路
                            tmpDetourPath = n.getShortedPathWithOneBrokenLinkAndFlowMatrix(break_start_node + 1, break_end_node, flowMatrix)
                            # print("有路可走", tmpDetourPath)
                            # print(f"算路{break_start_node + 1}, {break_end_node} --> {tmpDetourPath}")
                            if(len(tmpDetourPath)==0):
                                hasDetourPath = False
                                # print("无路可走")
                                break
                            # 将负载施加在这个流上
                            for k in range(len(tmpDetourPath) - 1):
                                # k & k+1
                                flowMatrix[tmpDetourPath[k] - 1][tmpDetourPath[k + 1] - 1] += traffic_matrix[i][j]
                                now_flow = flowMatrix[tmpDetourPath[k] - 1][tmpDetourPath[k + 1] - 1]
                                link_capacity = n.getLinkCapacity(tmpDetourPath[k], tmpDetourPath[k + 1])
                                alpha = (now_flow / link_capacity) if alpha < (now_flow / link_capacity) else alpha
                    if(not hasDetourPath):
                       continue
                    # 将路径上的路径都加上流大小
                    for k in range(len(tmpShortestPath) - 1):
                        if(tmpShortestPath[k] == break_start_node + 1 and tmpShortestPath[k + 1] == break_end_node):
                            # 跳过这条断路
                            continue
                        # k & k+1
                        flowMatrix[tmpShortestPath[k] - 1][tmpShortestPath[k + 1] - 1] += traffic_matrix[i][j]
                        now_flow = flowMatrix[tmpShortestPath[k] - 1][tmpShortestPath[k + 1] - 1]
                        link_capacity = n.getLinkCapacity(tmpShortestPath[k], tmpShortestPath[k + 1])
                        alpha = (now_flow / link_capacity) if alpha < (now_flow / link_capacity) else alpha
            for link_start_node in range(nodeNum):
                for link_end_node in n.getNode(link_start_node + 1).adjacentNodes:
                    # link_start_node + 1
                    # link_end_node
                    resourceUtilization += flowMatrix[link_start_node][link_end_node - 1] / n.getLinkCapacity(link_start_node + 1, link_end_node)
            if(hasDetourPath):
                alphas.append(alpha)
                resourceUtilizations.append(resourceUtilization)

    return np.mean(alphas), np.mean(resourceUtilizations) / np.mean(alphas) / n.getLinkNums(), calculate_jain_fairness(resourceUtilizations)

# 输入网络n和输入业务量矩阵matrix
def spf(n: QKDNetwork, traffic_matrix):
    # 依次断开每条边，测各种性能指标
    # 直接最短路径，输入网络和业务量矩阵，输出alpha的值（最大能满足的业务量系数）
    nodeNum = len(n.nodeList)
    # print(n.getLinkCapacity(1,2))
    alphas = []
    resourceUtilizations = []
    for break_start_node in range(nodeNum):
        for break_end_node in n.getNode(break_start_node + 1).adjacentNodes:
            flowMatrix = [[0] * nodeNum for _ in range(nodeNum)]
            alpha = 0
            resourceUtilization = 0  # 资源利用率
            hasDetourPath = True
            if(break_start_node + 1 == break_end_node):
                # 如果破碎的起点等于终点，则跳过
                continue
            # 破碎的起点ID：break_start_node + 1
            # 破碎的终点ID：break_end_node
            for i in range(nodeNum):
                for j in range(nodeNum):
                    if(i == j):
                        continue
                    tmpShortestPath = n.getShortedPath(i + 1, j + 1) # 获取最短路径
                    tmpShortestPath.insert(0, i + 1)

                    for spIndex in range(len(tmpShortestPath) - 1):
                        if(tmpShortestPath[spIndex] == break_start_node + 1 and tmpShortestPath[spIndex + 1] == break_end_node):
                            # 如果路断了，那么从断开处再计算一个最短路
                            tmpDetourPath = n.getShortedPathWithOneBrokenLink(break_start_node + 1, break_end_node)
                            # print("有路可走", tmpDetourPath)
                            # print(f"算路{break_start_node + 1}, {break_end_node} --> {tmpDetourPath}")
                            if(len(tmpDetourPath)==0):
                                hasDetourPath = False
                                # print("无路可走")
                                break
                            # 将负载施加在这个流上
                            for k in range(len(tmpDetourPath) - 1):
                                # k & k+1
                                flowMatrix[tmpDetourPath[k] - 1][tmpDetourPath[k + 1] - 1] += traffic_matrix[i][j]
                                now_flow = flowMatrix[tmpDetourPath[k] - 1][tmpDetourPath[k + 1] - 1]
                                link_capacity = n.getLinkCapacity(tmpDetourPath[k], tmpDetourPath[k + 1])
                                alpha = (now_flow / link_capacity) if alpha < (now_flow / link_capacity) else alpha
                    if(not hasDetourPath):
                       continue
                    # 将路径上的路径都加上流大小
                    for k in range(len(tmpShortestPath) - 1):
                        if(tmpShortestPath[k] == break_start_node + 1 and tmpShortestPath[k + 1] == break_end_node):
                            # 跳过这条断路
                            continue
                        # k & k+1
                        flowMatrix[tmpShortestPath[k] - 1][tmpShortestPath[k + 1] - 1] += traffic_matrix[i][j]
                        now_flow = flowMatrix[tmpShortestPath[k] - 1][tmpShortestPath[k + 1] - 1]
                        link_capacity = n.getLinkCapacity(tmpShortestPath[k], tmpShortestPath[k + 1])
                        alpha = (now_flow / link_capacity) if alpha < (now_flow / link_capacity) else alpha
            for link_start_node in range(nodeNum):
                for link_end_node in n.getNode(link_start_node + 1).adjacentNodes:
                    # link_start_node + 1
                    # link_end_node
                    resourceUtilization += flowMatrix[link_start_node][link_end_node - 1] / n.getLinkCapacity(link_start_node + 1, link_end_node)
            if(hasDetourPath):
                alphas.append(alpha)
                resourceUtilizations.append(resourceUtilization)

    return np.mean(alphas), np.mean(resourceUtilizations) / np.mean(alphas) / n.getLinkNums(), calculate_jain_fairness(resourceUtilizations)

def cplexResult(network:QKDNetwork, traffic_matrix, path_selection, fix_selection):
    # 依次断开每条边，测各种性能指标
    # 算一下求解器的方案的业务量系数
    nodeNum = len(network.nodeList) + 1 # 节点的个数
    
    alphas = []
    resourceUtilizations = []
    for break_start_node in range(nodeNum):
        if(break_start_node == 0):
            continue
        for break_end_node in network.getNode(break_start_node).adjacentNodes:
            flowMatrix = [[0] * nodeNum for _ in range(nodeNum)]
            alpha = 0
            resourceUtilization = 0  # 资源利用率
            hasDetourPath = False
            # 破碎的起点ID：break_start_node
            # 破碎的终点ID：break_end_node
            for m in range(nodeNum):                        # 业务量起点
                for n in range(nodeNum):                    # 业务量终点
                    for u in range(nodeNum):                # 业务边起点
                        for v in range(nodeNum):            # 业务边终点
                            if(path_selection[m][n][u][v] == 1):
                                if(break_start_node == u and break_end_node == v):
                                    # 如果是破碎节点，那么就按照Cplex的分流结果来计算
                                    for i in range(nodeNum):        # 分流边起点
                                        for j in range(nodeNum):    # 分流边终点
                                            if(fix_selection[m][n][u][v][i][j] != 0):
                                                hasDetourPath = True
                                                flowMatrix[i][j] += traffic_matrix[m - 1][n - 1] * fix_selection[m][n][u][v][i][j]
                                                link_capacity_ij = network.getLinkCapacity(i, j)
                                                alpha = (flowMatrix[i][j] / link_capacity_ij) if alpha < (flowMatrix[i][j] / link_capacity_ij) else alpha
                                else:
                                    # 如果不是破碎节点，那么就按照原来的路径来计算
                                    flowMatrix[u][v] += traffic_matrix[m - 1][n - 1]
                                    link_capacity_uv = network.getLinkCapacity(u, v)
                                    alpha = (flowMatrix[u][v] / link_capacity_uv) if alpha < (flowMatrix[u][v] / link_capacity_uv) else alpha
            for link_start_node in range(nodeNum - 1):
                for link_end_node in network.getNode(link_start_node + 1).adjacentNodes:
                    # link_start_node + 1
                    # link_end_node
                    resourceUtilization += flowMatrix[link_start_node + 1][link_end_node] / network.getLinkCapacity(link_start_node + 1, link_end_node)
            if(hasDetourPath):
                # 如果有分流方案，则此Alpha值有效
                alphas.append(alpha)
                resourceUtilizations.append(resourceUtilization)
                            
    return np.mean(alphas), np.mean(resourceUtilizations) / np.mean(alphas) / network.getLinkNums(), calculate_jain_fairness(resourceUtilizations)
